package br.ufrrj.im.redes.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import br.ufrrj.im.redes.logic.NumberTheory;
import br.ufrrj.im.redes.model.Message;
import br.ufrrj.im.redes.model.PlayMessage;
import br.ufrrj.im.redes.model.StartGameMessage;
import br.ufrrj.im.redes.model.UpdateGameMessageForGamer;
import br.ufrrj.im.redes.model.UpdateGameMessageForSpectator;
import br.ufrrj.im.redes.model.User;
import br.ufrrj.im.redes.util.Constants;

public class PersistentConnectionServer implements Runnable {
	
	private Socket persistentConnectionSocket;
	private User userTo;
	private Server server;
	private ObjectOutputStream streamToUser;
	private ObjectInputStream streamFromUser;
	
	private PlayMessage msg;
	private Message updateMessage;
	

	public PersistentConnectionServer(User userTo, Server server) {
		// TODO Auto-generated constructor stub
		this.userTo = userTo;
		this.server = server;
	}

	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		persistentConnectionSocket = new Socket();
		
		try {
			
			//System.out.println(new InetSocketAddress(userTo.getIp(), Constants.PORT_GAME_CONNECTION));
			//(servidor) cria um socket para (gerenciar) o usuário
			persistentConnectionSocket.connect(new InetSocketAddress(userTo.getIp(), Constants.PORT_GAME_CONNECTION), Constants.PERSISTENT_CONNECTION_TIME_OUT);			
			
			//aguarda o usuário(servidor) inicializar o jogo			
			while(!server.isGameStarted());

			//envia uma mensagem para o usuário dizendo que o jogo foi inicializado
			sendMessage(new StartGameMessage(defineRangePrime()));
						

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			//caso não seja possível se conectar ao usuário, então ele encerrou sua conexão
			this.server.removeUserOfListAvaibleUsers(this.userTo.getName());
			
			//encerra as conexões e os streams
			try {
				closeConnection();
				closeStreams();
				
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		
		
		//recebe jogadas do jogador em questão
		while(persistentConnectionSocket.isConnected()){
			
			try {
				streamFromUser = new ObjectInputStream(persistentConnectionSocket.getInputStream());
				
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				
				msg = validatePlayMessage(streamFromUser.readObject());
				
				//envia atualizações para todos os jogadores
				updateMessage = createUpdateMessageForGamersDefault();		
				server.sendUpdateToAllGamers((UpdateGameMessageForGamer) updateMessage);

				//envia atualizações para todos os espectadores
				updateMessage = createUpdateMessageForSpectatorsDefault();		
				server.sendUpdateToAllSpectators((UpdateGameMessageForSpectator) updateMessage);
				
				
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		try {
			closeConnection();
			closeStreams();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			this.server.removeUserOfListAvaibleUsers(this.userTo.getName());
		}
		
		
		
		
	}
	
	public void sendMessage(Message msg) throws IOException{

		streamToUser = new ObjectOutputStream(persistentConnectionSocket.getOutputStream());
		streamToUser.writeObject(msg);
		
		System.out.println("Message to user (" + this.userTo.getIp() + ") " + this.userTo.getName() + " sended with sucess.");

	}
	
	private UpdateGameMessageForGamer createUpdateMessageForGamersDefault(){
		UpdateGameMessageForGamer updateMessage = new UpdateGameMessageForGamer();
		updateMessage.setUpdate("Jogador: " + this.userTo.getName() + " selecionou um novo primo.");
		
		return updateMessage;
	}
	
	private UpdateGameMessageForSpectator createUpdateMessageForSpectatorsDefault(){
		UpdateGameMessageForSpectator updateMessage = new UpdateGameMessageForSpectator();
		
		//aqui tem que definir o que será enviado
		
		return updateMessage;
	}

	
	
	private PlayMessage validatePlayMessage(Object object){
		return (object instanceof PlayMessage) ? ((PlayMessage) object) : null;
	}
	
	public PlayMessage recivePlays(){
		return msg;
	}
	
	public void closeConnection() throws IOException {
		persistentConnectionSocket.close();
	}
	
	public void closeStreams() throws IOException{
		if(streamFromUser != null)
			streamFromUser.close();
		if(streamToUser != null)
			streamToUser.close();
	}

	
	
	private int defineRangePrime(){
		return (int) NumberTheory.generateRandomValue(1, this.server.getRangePrime());
	}
	

	

}
